package com.d4a5.mapping.google
{
	import com.adobe.serialization.json.JSON;
	import com.d4a5.mapping.core.AmfFactory;
	import com.d4a5.mapping.core.AmfObject;
	import com.d4a5.mapping.core.MapObjectStateContainer;
	import com.google.maps.LatLng;
	import com.google.maps.LatLngBounds;
	import com.google.maps.interfaces.IOverlay;
	import com.google.maps.interfaces.IPane;
	import com.google.maps.overlays.OverlayBase;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.Dictionary;
	
	public class wmgArcgisDynamicMapServiceLayer implements IwmgLayer
	{
		
		protected var _name:String;
		protected var _image:wmgArcgisDynamicMapServiceOverlay;
		protected var _pane:IPane;
		
		protected var _selected:MapObjectStateContainer;
		protected var _mouseDownPoint:Point;
		protected var _renderer:wmgRenderer = new wmgRenderer();
		
		
		public var moveToTopOnSelect:Boolean = true;
		public var mouseWiggle:uint = 2;
		
		
		public function wmgArcgisDynamicMapServiceLayer() 
		{
			super();
			this._image = new wmgArcgisDynamicMapServiceOverlay();
			this._selected = new MapObjectStateContainer(MapObjectStateContainer.typeISelectedObject);
		}
		
		
		public function get url() : String {
			return this._image.url;
		}
		public function set url( u:String ) : void {
			this._image.url = u;
		}
		
		public function get name() : String {
			return this._name;
		}
		public function set name( u:String ) : void {
			this._name = u;
		}
		
		public function get layers() : Array {
			return this._image.layers;
		}
		public function set layers( a:Array ) : void {
			this._image.layers = a;
		}
		
		public function get pane() : IPane {
			return this._pane;
		}
		public function set pane( p:IPane ) : void {
			if( this._pane == null && p != null ){
				this._pane = p;
				
				if( this.selectedOptions ){
					this.pane.map.addEventListener(MouseEvent.MOUSE_DOWN,OnMouseDown);
					this.pane.map.addEventListener(MouseEvent.CLICK,onMouseClick);
				}
				
				this._pane.map.addOverlay( this._image );
			}
		}
		
		public function get alpha() : Number {
			return this._image.alpha;
		}
		public function set alpha( a:Number ) : void {
			this._image.alpha = a;
		}
		
		
		public function get multiselect() : Boolean {
			return this._selected.allowMultiples;
		}
		public function set multiselect( m:Boolean ) :  void {
			this._selected.allowMultiples = m;
		}
		
		public function get renderer() : wmgRenderer {
			return this._renderer;
		}
		public function set renderer( r:wmgRenderer ) :  void {
			this._renderer.innerRenderer = r;
		}
		
		public function get selected() : MapObjectStateContainer {
			return this._selected;
		}
		
		public function get selectedOptions() : Object {
			return this._renderer.selectedOptions;
		}
		public function set selectedOptions( options:Object ) : void {
			this._renderer.selectedOptions = options;
			
			if( this.pane && this.pane.map ){
				if( options ){
					this.pane.map.addEventListener(MouseEvent.MOUSE_DOWN,OnMouseDown);
					this.pane.map.addEventListener(MouseEvent.CLICK,onMouseClick);
				}
				else{
					this.pane.map.removeEventListener(MouseEvent.MOUSE_DOWN,OnMouseDown);
					this.pane.map.removeEventListener(MouseEvent.CLICK,onMouseClick);
				}
			}
		}
		
		protected function onGraphicSelected( event:Event ) : void {
			this._selected.addItem( event.target );
			
			if( this.moveToTopOnSelect ){
				this.moveInStack( event.target as wmgBaseOverlay );
			}
		}
		
		protected function onGraphicDeSelected( event:Event ) : void {
			this._selected.removeItem( event.target );
		}
		
		public function moveInStack( g:wmgBaseOverlay ) : void {
			var i:int;
			var length:uint;
			var current:wmgBaseOverlay;
			
			
			length = this._selected.length
			for( i = 0; i < length; i++ ){
				current = this._selected[i];
				this.pane.bringToTop( current as IOverlay );
			}
			
			if( g.selected && g != current ){
				this.pane.bringToTop( g as IOverlay );
			}
		}
		
		protected function OnMouseDown( event:MouseEvent ) : void {
			this._mouseDownPoint = new Point( event.stageX , event.stageY );
		}
		
		protected function onMouseClick( event:MouseEvent ) : void {
			if( event.target is OverlayBase ){
				if( Point.distance( this._mouseDownPoint , new Point( event.stageX , event.stageY ) ) < this.mouseWiggle ){
					this.selectGraphicsUnderPt( event );
				}
			}
			else{
				this.selected.removeAll();
			}
		}
		
		protected function selectGraphicsUnderPt( ptData:MouseEvent ) : void {
			var ll:LatLng = this.pane.map.fromViewportToLatLng( new Point( ptData.localX , ptData.localY ) );
			
			var identify:URLLoader = new URLLoader();
			identify.addEventListener(Event.COMPLETE,onIdentifyComplete);
			identify.addEventListener(IOErrorEvent.IO_ERROR,onIdentifyFail);
			identify.load( this.queryParameterizedUrl( ll ) );
		}
		
		public function refresh( fullRefresh:Boolean = false ) : void {
			this._image.positionOverlay( fullRefresh );
		}
		
		
		public function get serviceInfo() : Object {
			return this._image.serviceInfo;
		}
		
		
		protected function queryParameterizedUrl(ll:LatLng) : URLRequest {
			var url:String = this.url + "/identify?";
			
			url += "f=json&";
			url += "geometry=" + ll.lng().toString() + "%2C" + ll.lat().toString() + "&";
			url += "geometryType=esriGeometryPoint&";
			url += "sr=4326&";
			
			url += "tolerance=" + this.mouseWiggle.toString() + "&";
			url += "returnGeometry=true&";
			
			url += "mapExtent=" + this._image.mapExtentString + "&";
			url += "imageDisplay=" + this._image.imageWidth.toString() + "%2C" + this._image.imageHeight.toString() + "%2C" + this._image.dpi.toString();
			
			return new URLRequest( url );	
		}
		
		protected function onIdentifyComplete( event:Event ) : void {
			var obj:Object = JSON.decode( event.target.data as String , true );
			if( obj && obj.results is Array ){
				var results:Array = obj.results as Array;
				var numResults:int = results.length;
				for( var i:int = 0; i < numResults; i++ ){
					var result:Object = results[i];
					var type:int = ( result.geometryType == "esriGeometryPolygon" ) ? AmfFactory.typePolygon : 0;
					var isGeographic:Boolean = ( result.geometry.spatialReference.wkid == "4326" );
					var amf:AmfObject = AmfFactory.Create( AmfFactory.formatESRIJSON , type , result.geometry.rings , null , true , isGeographic );
				}
			}
			
			
		}
		protected function onIdentifyFail( event:Event ) : void {
			
		}
	}
}





import com.adobe.serialization.json.JSON;
import com.d4a5.mapping.google.wmgBaseOverlay;
import com.google.maps.LatLng;
import com.google.maps.LatLngBounds;
import com.google.maps.Map;
import com.google.maps.MapEvent;
import com.google.maps.MapMouseEvent;
import com.google.maps.MapMoveEvent;
import com.google.maps.interfaces.IOverlay;
import com.google.maps.interfaces.IPane;
import com.google.maps.overlays.GroundOverlay;
import com.google.maps.overlays.OverlayBase;

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLVariables;
import flash.utils.Dictionary;

import mx.events.ResizeEvent;

class wmgArcgisDynamicMapServiceOverlay extends wmgBaseOverlay
{
	
	protected var _url:String;
	protected var _serviceInfo:Object;
	protected var _image:Loader = new Loader();
	protected var _pullingNewImage:Boolean = false;
	protected var _matrix:Matrix;
	protected var _ul:Point;
	protected var _lr:Point;
	protected var _newUl:Point;
	protected var _newLr:Point;
	protected var _currentBounds:LatLngBounds;
	protected var _mapExtentString:String = "";
	protected var _layers:Array;
	
	
	public function wmgArcgisDynamicMapServiceOverlay()
	{
		this.addEventListener(Event.ENTER_FRAME,onEnterFrame);
		
		this._image.contentLoaderInfo.addEventListener(Event.COMPLETE,onImageLoaderComplete);
		this._image.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,onImageLoaderFail);
	}
	
	protected function onEnterFrame( event:Event ) : void {
		if( this.pane && this.pane.map ){
			this.removeEventListener(Event.ENTER_FRAME,onEnterFrame);
			this.pane.map.addEventListener(MapMoveEvent.MOVE_END,onMapChange);
			this.pane.map.addEventListener(ResizeEvent.RESIZE,onMapChange);
		}
	}
	
	
	override protected function commitProperties():void {
		if( this._pullingNewImage ){
			this._image.load( this.parameterizedUrl );
		}
	}
	
	
	protected function onMapChange( event:Event ) : void {
		this.pullNewImage();
	}
	
	override public function positionOverlay(arg0:Boolean):void {
		if( this.pane != null ){
			if( this._matrix == null ){
				this._matrix = this.getMatrix();
			}
			else{
				this.getMatrix();
			}
			
			if( arg0 ){
				this.graphics.clear();
				this.pullNewImage();
			}
			else{
				this.x = this._ul.x - (this._matrix.tx + ( this._matrix.a * wmgBaseOverlay.currentMapBounds.x.min ));
				this.y = this._ul.y - (this._matrix.ty + ( this._matrix.d * wmgBaseOverlay.currentMapBounds.y.max ));
				//trace( wmgBaseOverlay.currentMapBounds );
				//trace( this.pane.map.getLatLngBounds() );
				//trace( this.x.toString() + "," + this.y.toString() );
			}
		}
	}
	
	protected function pullNewImage() : void {
		if( this._serviceInfo ){
			if( this._pullingNewImage == false ){
				this._pullingNewImage = true;
				this.invalidateProperties();
			}
		}
	}
	
	
	protected function get parameterizedUrl() : URLRequest {
		var url:String = this._url + "/export?";
		
		this._currentBounds = this.pane.map.getLatLngBounds();
		
		var east:Number = this._currentBounds.getEast();
		var west:Number = this._currentBounds.getWest();
		var north:Number = this._currentBounds.getNorth();
		var south:Number = this._currentBounds.getSouth();
		var center:Number = this._currentBounds.getCenter().lng();
		
		if( center < west ){
			west = -180;
		}
		else if( center > east ){
			east = 180;
		}
		
		if( north > 85 ){
			north = 85;
		}
		if( south < -85){
			south = -85;
		}
		
		var n:String = north.toString();
		var s:String = south.toString();
		var e:String = east.toString();
		var w:String = west.toString();
		
		
		this._newUl = this.pane.map.fromLatLngToViewport( new LatLng( north , west ) , true );
		this._newLr = this.pane.map.fromLatLngToViewport( new LatLng( south , east ) , true );
		
		var activeMapWidth:String = int( this._newLr.x - this._newUl.x ).toString();
		var activeMapHeight:String = int( this._newLr.y - this._newUl.y ).toString();
		
		
		url += "f=image&";
		url += "transparent=true&";
		url += "imageSR=102100&";
		url += "bboxSR=4326&";
		
		if( this._serviceInfo.singleFusedMapCache ){
			url += "dpi=" + this._serviceInfo.tileInfo.dpi + "&";
			url += "format=" + this._serviceInfo.tileInfo.format + "&";
		}
		else{
			if( this._layers && this._layers.length > 0 ){
				url += "layers=show:" + this._layers.join( "," ) + "&";
			}
			url += "dpi=96&";
			url += "format=PNG32&";
		}
		
		url += "size=" + activeMapWidth + "%2C" + activeMapHeight + "&";
		url += "bbox=" + w + "%2C" + s + "%2C" + e + "%2C" + n;
		
		this._mapExtentString = w + "%2C" + s + "%2C" + e + "%2C" + n;
		
		return new URLRequest( url );	
	}
	
	protected function onImageLoaderComplete( event:Event ) : void {
		this.draw( this.getMatrix() );
	}
	
	protected function onImageLoaderFail( event:IOErrorEvent ) : void {
		this.graphics.clear();
		throw new Error("ERROR : wmgArcgisDynamicMapServiceOverlay : Failed to get Image : from(" + this._url + ")"  );
		this._drawn = false;
		this._pullingNewImage = false;
	}
	
	
	override protected function draw(m:Matrix):void {
		if( this.pane.map.getLatLngBounds().equals( this._currentBounds ) ){
			this._matrix = this.getMatrix();
			this.graphics.clear();
			this._drawn = false;
			
			this.graphics.lineStyle( 2 , 0xff0000 , 1 , true );
			this.graphics.beginBitmapFill( (this._image.content as Bitmap).bitmapData );
			this.graphics.drawRect( 0 , 0 , this._image.content.width , this._image.content.height );
			this.graphics.endFill();
			
			this._ul = this._newUl;
			this._lr = this._newLr;
			
			this.x = this._ul.x;
			this.y = this._ul.y;
			
			this._drawn = true;
			this._pullingNewImage = false;
		}
		else{
			trace( "repull" );
			this._pullingNewImage = false;
			this.pullNewImage();
		}
	}
	
	
	
	
	
	
	public function get url() : String {
		return this._url;
	}
	public function set url( u:String ) : void {
		this._url = u;
		this.getServiceInfo();
	}
	
	public function get layers() : Array {
		return this._layers;
	}
	public function set layers( a:Array ) : void {
		this._layers = a;
		
		if( this.pane && this.pane.map ){
			this.refresh();
		}
	}
	
	protected function getServiceInfo() : void {
		this._serviceInfo = null;
		
		if( this._url ){
			var loader:URLLoader = new URLLoader();
			var request:URLRequest = new URLRequest( this._url + "?f=json" );
			
			loader.addEventListener(Event.COMPLETE,onGotServiceInfo);
			loader.addEventListener(IOErrorEvent.IO_ERROR,onFailedToGetServiceInfo);
			
			loader.load( request );
		}
	}
	
	protected function onGotServiceInfo( event:Event ) : void {
		this._serviceInfo = JSON.decode( event.target.data as String , true );
		
		if( (this._serviceInfo.capabilities as String).match( "Map" ) == false ){
			throw new Error("Error : wmgArcgisDynamicMapServiceOverlay : Service is not map capable");
		}
		
		if( this.pane && this.pane.map ){
			this.refresh();
		}
	}
	protected function onFailedToGetServiceInfo( event:IOErrorEvent ) : void {
		throw new Error("ERROR : wmgArcgisDynamicMapServiceOverlay : Failed to get Service Info : from(" + this._url + ")"  );
	}
	
	public function get serviceInfo() : Object {
		return this._serviceInfo;
	}
	
	public function get imageWidth() : Number {
		return this._image.content.width;
	}
	public function get imageHeight() : Number {
		return this._image.content.height;
	}
	public function get dpi() : int {
		if( this._serviceInfo && this._serviceInfo.tileInfo ){
			return this._serviceInfo.tileInfo.dpi;
		}
		return 96;
	}
	public function get mapExtentString() : String {
		return this._mapExtentString;
	}
	
}

